﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Reflection;

namespace Common
{
    // 定义服务生命周期的枚举
    public enum EServLife
    {
        // 单例服务，一个实例
        Singleton = ServiceLifetime.Singleton,
        // 每个请求使用一个新的实例
        Scoped = ServiceLifetime.Scoped,
        // 每次请求都创建新的实例
        Transient = ServiceLifetime.Transient,
    }

    // 自定义属性，用于标记服务类型及其生命周期
    [AttributeUsage(AttributeTargets.Class)]
    public class ServiceAttribute : Attribute
    {
        public EServLife Lifetime { get; set; }
        public ServiceAttribute(EServLife lifetime)
        {
            Lifetime = lifetime;
        }
    }

    // 批量注册服务的静态类
    public static class ServiceBatchRegister
    {
        // 批量注册服务方法
        public static void BatchRegisterServices(this IServiceCollection services)
        {
            var allAssembly = GetAppAssembly();

            // 按生命周期批量注册服务
            services.RegisterServiceByAttribute(EServLife.Singleton, allAssembly);
            services.RegisterServiceByAttribute(EServLife.Scoped, allAssembly);
            services.RegisterServiceByAttribute(EServLife.Transient, allAssembly);

            // 注册后台服务
            services.RegisterBackgroundService(allAssembly);
        }

        // 根据 ServiceAttribute 特性批量注册服务
        private static void RegisterServiceByAttribute(this IServiceCollection services, EServLife serviceLifetime, List<Assembly> allAssembly)
        {
            // 获取所有带有 ServiceAttribute 的类型
            List<Type> types = allAssembly.SelectMany(t => t.GetTypes())
                .Where(t => t.GetCustomAttributes(typeof(ServiceAttribute), false).Length > 0
                    && t.GetCustomAttribute<ServiceAttribute>()?.Lifetime == serviceLifetime
                    && t.IsClass && !t.IsAbstract).ToList();

            foreach (var type in types)
            {
                Type? typeInterface = type.GetInterfaces().FirstOrDefault();
                if (typeInterface == null)
                {
                    // 如果没有接口，直接按类型注入
                    switch (serviceLifetime)
                    {
                        case EServLife.Singleton: services.AddSingleton(type); break;
                        case EServLife.Scoped: services.AddScoped(type); break;
                        case EServLife.Transient: services.AddTransient(type); break;
                    }
                }
                else
                {
                    // 如果继承了接口，按接口和实现类一起注入
                    switch (serviceLifetime)
                    {
                        case EServLife.Singleton: services.AddSingleton(typeInterface, type); break;
                        case EServLife.Scoped: services.AddScoped(typeInterface, type); break;
                        case EServLife.Transient: services.AddTransient(typeInterface, type); break;
                    }
                }
            }
        }

        // 注册后台服务
        private static void RegisterBackgroundService(this IServiceCollection services, List<Assembly> allAssembly)
        {
            List<Type> types = allAssembly.SelectMany(t => t.GetTypes())
                .Where(t => typeof(BackgroundService).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract).ToList();

            foreach (var type in types)
            {
                services.AddSingleton(typeof(IHostedService), type);
            }
        }

        // 获取所有相关的 Assembly
        private static List<Assembly> GetAppAssembly()
        {
            // 获取 Common 程序集
            var commonAssembly = typeof(ServiceAttribute).Assembly;
            List<Assembly> appAssemblies = new() { commonAssembly };

            // 获取所有已加载的程序集
            Queue<Assembly> assembliesToCheck = new();
            HashSet<string> loadedAssemblies = new();
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                assembliesToCheck.Enqueue(assembly);
                loadedAssemblies.Add(assembly.FullName!);
            }

            // 遍历引用了 Common 的程序集
            while (assembliesToCheck.Any())
            {
                var assemblyToCheck = assembliesToCheck.Dequeue();
                var fullname = assemblyToCheck.FullName!;
                if (fullname.StartsWith("System") || fullname.StartsWith("Microsoft"))
                {
                    continue;
                }
                bool isCommonReferenced = false;
                foreach (var reference in assemblyToCheck.GetReferencedAssemblies())
                {
                    if (!loadedAssemblies.Contains(reference.FullName))
                    {
                        var assembly = Assembly.Load(reference);
                        assembliesToCheck.Enqueue(assembly);
                        loadedAssemblies.Add(reference.FullName);
                    }
                    isCommonReferenced |= (reference.FullName == commonAssembly.FullName);
                }
                if (isCommonReferenced) { appAssemblies.Add(assemblyToCheck); }
            }

            return appAssemblies;
        }
    }
}
